<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8">
    <title>Sucha's Blog - Archive for July, 2019</title>
    <meta name="generator" content="MarkdownProjectCompositor.lua">
    <meta name="author" content="Sucha">
    <meta name="keywords" content="suchang, programming, Linux, Lua">
    <meta name="description" content="Sucha's blog">
    <link rel="shortcut icon" href="../images/ico.png">
    <link rel="stylesheet" type="text/css" href="../styles/blog.css">
    <link rel="stylesheet" type="text/css" href="../styles/prism.min.css">
    <style id="site_theme"></style>
  </head>
  <body>
    <div id="body">
      <div id="text">
	   <!-- Page published by cmark-gfm begins here --><h1>Sucha's Blog ~ Archive for July, 2019</h1>
<p><a id="p1"></a></p>
<div class="date">19年7月13日 周六 23:34</div>
<h2>gmp_ffi.lua</h2>
<p>上周考虑的 GMP LuaJIT FFI 绑定，最后将 GMP 的绝大部分函数接口导了出来变成了 <a href="https://github.com/lalawue/gmp_ffi">gmp_ffi</a>，包括整数、浮点、有理数、随机数，以及格式化输出，除了那些 C 直接输出文件包含 FILE * 的部分，后续也可以考虑将标准 C 库的文件操作函数也一并导出来。</p>
<p>另外，还将相关的初始化函数，通过 ffi.gc() 跟 finalizer 关联了起来，就不需要自己做相关的清理工作了。</p>
<p>还有，GMP 的 C 接口，其内部实现，都是带 __gmp 这样前缀的，即便通过 FFI 使用，也很不方便，所以 gmp_ffi 库，将这些接口都重新做了绑定，重命名为类似 mpz_mul() 这样的函数，跟 gmp.h 上通过 C 宏定义出来的接口一样使用。</p>
<p>有了上面的铺垫，一些好玩的东西可以搞起来，比如粗糙的计算素数的程序 <a href="https://github.com/lalawue/gmp_ffi/blob/master/test_prime.lua">test_prime.lua</a>，比如温习一下 RSA 加解密 <a href="https://github.com/lalawue/gmp_ffi/blob/master/test_rsa.lua">test_rsa.lua</a>，当前的实现是到处抄的，遥望大学时虽有做这个的大习题，但早就忘了，-.-</p>
<p>贴一下 gmp_ffi.lua 使用的例子吧：</p>
<pre><code class="language-lua">local gmp = require(&quot;gmp_ffi&quot;)

function gmpffi_test()
   local a = gmp.mpz(11111111111)
   local b = gmp.mpz(&quot;999999999999999999999999999999999&quot;)
   local c = gmp.mpz(&quot;99999&quot;)
   gmp.mpz_mul(a, a, b)
   --gmp.mpz_set_ui(c, 1)
   gmp.printf(&quot;mpz: a:%Zd, c sign:&quot;, a)
   print(string.format(&quot;%d, odd:%s&quot;, gmp.mpz_sgn(c), gmp.mpz_odd(c)))
   a = gmp.mpz(&quot;ff&quot;, 16)
   gmp.printf(&quot;mpz: value base %Zx\n&quot;, a)

   a = gmp.mpf(111111111.111111111)
   b = gmp.mpf(&quot;999999999999999999999999999.99999999999999999999999999999999999&quot;)
   c = gmp.mpf(&quot;9999&quot;)
   gmp.mpf_mul(a, a, b)
   gmp.printf(&quot;mpf: %Ff, c sign:&quot;, a)
   print(gmp.mpf_sgn(c))

   a = gmp.mpq(111111111, 111111111)
   b = gmp.mpq(&quot;9999999999999999999999999999999/99999999999999999999999999999999999999&quot;)
   c = gmp.mpq(0)
   gmp.mpq_div(a, a, b)
   gmp.printf(&quot;mpq: %Qx, c sign:&quot;, a)
   print(gmp.mpq_sgn(c))

   local rt = gmp.randinit()
   local cs = gmp.cstring(64)
   gmp.sprintf(cs, &quot;random: %u&quot;, gmp.urandomb_ui(rt, 9999999))
   print(gmp.tostring(cs))
end

gmpffi_test()
</code></pre>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2019-07.html#p1">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<p><a id="p0"></a></p>
<div class="date">19年7月6日 周六 23:55</div>
<h2>LuaJIT FFI Library</h2>
<p>学习使用大数库，比如 <a href="https://gmplib.org/">GMP</a>，只是使用起来很糟心，比如大整数运算，先要 mpz_init() 最后回收时是 mpz_clear()，没错就是跟 malloc() 和 free() 一样的。就想着能不能稍微改善下易用性，用 C++ 是可以达到目的的，只是牛刀杀鸡，要走编译才能跑的还是太重了，而且 C++ 我也不熟。</p>
<p>就想着不如用 Lua 将其封装起来，之前的经验，是得自己写一个跟 Lua 虚拟机交互数据的接口，一个先进后出塞参数的栈，如果是要凑 table，还要复杂些。但如果使用 LuaJIT 的 FFI 库，就方便多了，具体在 BSD 这边是通过 dlsym 打开共享库，在 LuaJIT 脚本声明 C 接口后，通过 FFI 库直接调用。</p>
<p>能够支持各种 C 的函数接口，比如可变参数，以及各种数据类型，包括可变长度 struct 数据，甚至还有支持 Lua 侧的 gc 对于创建的 cdata 进行 finalizer，让 Lua 的 gc 只管理虚拟机这一侧 C 指针生命周期，API 文档是 <a href="http://luajit.org/ext_ffi_api.html">http://luajit.org/ext_ffi_api.html</a>。</p>
<h3>基本使用</h3>
<p>具体点的例子，比如声明并调用函数，创建一个 struct 结构，以及申请、释放内存的官方例子：</p>
<pre><code class="language-lua">local ffi = require(&quot;ffi&quot;)

ffi.cdef[[
int printf(const char *fmt, ...); //声明并调用标准 C 库函数
typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel; //struct 结构声明
]]

ffi.C.printf(&quot;Hello %s!&quot;, &quot;world&quot;)

-- 栈上 struct 结构
local img = ffi.new(&quot;rgbg_pixel[?]&quot;, 3)
img[0].green = 1

-- 创建、并释放外部内存对象
local p = ffi.gc(ffi.C.malloc(n), ffi.C.free)
...
p = nil -- Last reference to p is gone.
-- GC will eventually run finalizer: ffi.C.free(p)
</code></pre>
<h3>调用自定义库</h3>
<p>如果是自定义库，库路径需加入 LuaJIT 的 package.cpath 中，使用前 ffi.load('myffi') 一下，比如下面的例子看下 ffi.gc 是不是真的有用：</p>
<pre><code class="language-c">//#include &quot;stdio.h&quot;
//#include &quot;stdlib.h&quot;
void* test_malloc(int n) {
   void *p = malloc(n);
   printf(&quot;test_malloc %p:%d\n&quot;, p, n);
   return p;
}

void test_free(void *p) {
   printf(&quot;test_free %p\n&quot;, p);
   free(p);
}
</code></pre>
<p>先在本目录生成一个共享库：</p>
<pre><code class="language-bash">$ clang -g -o libmyffi.dylib -fpic -shared myffi.c
</code></pre>
<p>在 LuaJIT 脚本侧这样使用：</p>
<pre><code class="language-lua">local ffi = require(&quot;ffi&quot;)
local myffi = ffi.load(&quot;myffi&quot;)
ffi.cdef[[
void* test_malloc(int n);
void test_free(void *p);
]]
local p = ffi.gc(myffi.test_malloc(10), myffi.test_free)
print( p )
p = nil
</code></pre>
<p>会这样输出：</p>
<pre><code class="language-example">test_malloc 0x7f9962c027c0:10
cdata&lt;void *&gt;: 0x7f9962c027c0
test_free 0x7f9962c027c0
</code></pre>
<h3>使用 GMP 的例子</h3>
<p>先试一下 GMP 提供的大整数接口：</p>
<pre><code class="language-lua">local ffi = require(&quot;ffi&quot;)
local gmp = ffi.load(&quot;gmp&quot;)

ffi.cdef[[
int printf(const char *fmt, ...);

typedef unsigned long long int	mp_limb_t;

typedef struct
{
  int _mp_alloc;		/* Number of *limbs* allocated and pointed
				   to by the _mp_d field.  */
  int _mp_size;			/* abs(_mp_size) is the number of limbs the
				   last field points to.  If _mp_size is
				   negative this is a negative number.  */
  mp_limb_t *_mp_d;		/* Pointer to the limbs.  */
} __mpz_struct;

typedef const __mpz_struct *mpz_srcptr;
typedef __mpz_struct *mpz_ptr;
typedef __mpz_struct mpz_t[1];

void __gmpz_init(mpz_ptr);
int __gmpz_set_str(mpz_ptr, const char *, int);
void __gmpz_mul(mpz_ptr, mpz_srcptr, mpz_srcptr);

void __gmpz_clear(mpz_ptr);
int __gmp_printf (const char *, ...);
]]

ffi.C.printf(&quot;input n1 x %s:\n&quot;, &quot;n2&quot;)

local arg1, arg2 = ...
if not arg or not arg2 then
   os.exit(0)
end

local n1 = ffi.new(&quot;mpz_t&quot;)
local n2 = ffi.new(&quot;mpz_t&quot;)
local n3 = ffi.new(&quot;mpz_t&quot;)

gmp.__gmpz_init(n1)
gmp.__gmpz_init(n2)
gmp.__gmpz_init(n3)

gmp.__gmpz_set_str(n1, arg1, 10)
gmp.__gmpz_set_str(n2, arg2, 10)

gmp.__gmpz_mul(n3, n1, n2)

gmp.__gmp_printf(&quot;n1 = %Zd\n&quot;, n1)
gmp.__gmp_printf(&quot;n2 = %Zd\n&quot;, n2)
gmp.__gmp_printf(&quot;n1 * n2 = %Zd\n&quot;, n3)

gmp.__gmpz_clear(n1)
gmp.__gmpz_clear(n2)
gmp.__gmpz_clear(n3)
</code></pre>
<p>输出是：</p>
<pre><code class="language-example">$ luajit test_ffi.lua 11111111111111111111111 99999999999999999999999999999999
input n1 x n2:
n1 = 11111111111111111111111
n2 = 99999999999999999999999999999999
n1 * n2 = 1111111111111111111111099999999988888888888888888888889
</code></pre>
<p>后面再看下怎么封装 GMP 的接口，毕竟这么使用，只是少了一次编译的时间，接口函数还是太复杂。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2019-07.html#p0">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<!-- Page published by cmark-gfm ends here -->
  <div id="foot">2004-<script>var d = new
	Date();document.write(d.getFullYear())</script> &copy;
	Sucha. Powered by MarkdownProjectCompositor.
  </div>
  </div><!-- text -->
  <div id="sidebar">
  </div><!-- sidebar -->
  <script src="../js/prism.min.js" async="async"></script>
  <script src="../js/blog_sidebar.js"></script>
  </div> <!-- body -->
</body>
</html>